/** 
	@file  databasemonitor.cpp файл реализации класса для работы с базой данных
	@date  18.07.2012
	@author  Kazakov A.V.
 */

#include "../include/databasemonitor.h"
#include "../include/literaturecard.h"
#include "../include/searchparameter.h"
#include <QtSql>
#include <string>
#include <list>
#include <QMessageBox>

#define	MAX_CHAR_STR	400
#define	MAX_CHAR_ANN	1200

/******************************************************************************/

//	конструктор
DatabaseMonitor::DatabaseMonitor(QObject *parent)
	: QObject(parent)
{
	LiteratureBase = new QSqlDatabase();
	*LiteratureBase = QSqlDatabase::addDatabase("QSQLITE");
}

/******************************************************************************/

//	деструктор
DatabaseMonitor::~DatabaseMonitor()
{
	delete LiteratureBase;
}

/******************************************************************************/

// Создать базу данных
QString DatabaseMonitor::createDataBase(QString qstrDataBaseName) 
{
	LiteratureBase->setDatabaseName(qstrDataBaseName);
	
	//	открываем базу данных
	if(!openDataBase())
		throw	CANT_OPEN_DATABASE;

	//	формируем запрос на создание таблицы
	QString	qstrQueryCreate;
	qstrQueryCreate.sprintf("create table %s "
							"("
							"%s integer primary key,"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d),"
							"%s varchar(%d)"
							")",
							S_TABLENAME, S_ID,
							S_TITLE, MAX_CHAR_STR,
							S_AUTHOR, MAX_CHAR_STR,
							S_ANNOTATION, MAX_CHAR_ANN,
							S_PUBLICDATE, MAX_CHAR_STR,
							S_ADDDATE, MAX_CHAR_STR,
							S_SOURCE, MAX_CHAR_STR,
							S_SPHERE, MAX_CHAR_STR,
							S_FIELD, MAX_CHAR_STR,
							S_FILENAME, MAX_CHAR_STR,
							S_FILEPATH, MAX_CHAR_STR);

	QSqlQuery	createQuery;
	if(!createQuery.exec(qstrQueryCreate))	//	ошибка запроса
	{
		//	проверим существует ли такая база данных
		//	errorType = QSqlError::StatementError
		if(createQuery.lastError().type() == QSqlError::StatementError)
		{
			//	такая таблица существует, удаляем её
			try
			{
				closeDataBase();
				
				if(!deleteTable())
					return	QString("");

				//	повторно передаем запрос

				return	createDataBase(qstrDataBaseName);
			}
			catch(...)
			{
				throw;
			}
		}
		else	//	другая ошибка
			throw	CANT_CREATE_DB_EX;
	}

	//	закрываем соединение
	closeDataBase();

	return	qstrDataBaseName;
}

/******************************************************************************/

void DatabaseMonitor::setDataBaseFileName(QString qstrDataBaseFileName)
{
	//	проверяем существует ли файл?
	QFileInfo fileInfo(qstrDataBaseFileName);
	if(!fileInfo.exists())
		throw	int(FILE_NOT_EXIST_EX);

	//	открываем базу данных
	LiteratureBase->setDatabaseName(qstrDataBaseFileName);
	if(!openDataBase())
		throw	CANT_OPEN_DATABASE;

	//	все прошло успешно, закрываем базу данных
	closeDataBase();
}

/******************************************************************************/

LiteratureCard DatabaseMonitor::getCard(int id)
{
	//	запрос на выдачу карточки из базы данных

	//	Формируем строку запроса
	QString	qstrRequest;
	qstrRequest.sprintf("select * from %s where %s=%d",
					 S_TABLENAME, S_ID, id);
	
	//	запрос к базе данных

	//	открываем базу данных
	if(!openDataBase())
		throw	CANT_OPEN_DATABASE;
	
	//	получение карточки
	QSqlQuery	createQuery;
	if(!createQuery.exec(qstrRequest))
		throw	QUERY_ERROR_EX;

//	if(createQuery.size() < 1)
//		throw	CARD_NOT_EXIST_EX;
	
	//	составление карточки
	if(!createQuery.next())
		throw	CARD_NOT_EXIST_EX;

	QSqlRecord	rec = createQuery.record();

	//	получаем поля
	QString		title = createQuery.value(
		rec.indexOf(S_TITLE)).toString();
	QString		filename = createQuery.value(
		rec.indexOf(S_FILENAME)).toString();
	QDate       adddate = createQuery.value(
		rec.indexOf(S_ADDDATE)).toDate();
	QDate       publicdate = createQuery.value(
		rec.indexOf(S_PUBLICDATE)).toDate();
	QString     annotation = createQuery.value(
		rec.indexOf(S_ANNOTATION)).toString();
	QString     sphere = createQuery.value(
		rec.indexOf(S_SPHERE)).toString();
	QString     field = createQuery.value(
		rec.indexOf(S_FIELD)).toString();
	QString     source = createQuery.value(
		rec.indexOf(S_SOURCE)).toString();
	QString     filepath = createQuery.value(
		rec.indexOf(S_FILEPATH)).toString();
	
	//	закрываем соединение
	closeDataBase();

	//	Создаем и заполняем карточку
	LiteratureCard	card(id, 
						 title,
						 filename,
						 adddate,
						 publicdate,
						 annotation,
						 sphere,
						 field,
						 source,
						 filepath);
	//	передача карточки
	return	card;
}

/******************************************************************************/

bool DatabaseMonitor::writeCard(LiteratureCard card)
{
	//	производим запись информационной карты литературы в базу данных
	bool	bResult = true;
	//	есть ли у карточки id?
	if(card.getID() >= 0)
	{
		bool	bCardExist = true;
		
		try
		{
			//	проверка карточки в базе данных
			getCard(card.getID());
		}
		catch(int nEx)
		{
			if(nEx == CARD_NOT_EXIST_EX)	//	карточки нет в базе
				bCardExist = false;
			else
				throw;
		}
			
		if(bCardExist)
			return	updateCard(card);
	}

	//	открываем базу данных
	if(!openDataBase())
		throw	CANT_OPEN_DATABASE;

	//	получаем максимальный id
	QString	qstrRequest;
	qstrRequest.sprintf("select max(%s) from %s", S_ID, S_TABLENAME);
	//	производим запрос
	QSqlQuery	queryAdd;
	if(!queryAdd.exec(qstrRequest))
		throw	CANT_WRITE_DTBASE;
	
	int	id = 1;
	//	проверяем есть ли данный id
	if(queryAdd.next())
	{
		id = queryAdd.value(0).toInt();
		id++;

		bResult = true;
	}

//						S_TABLENAME, S_ID,
//						S_TITLE
//						S_AUTHOR
//						S_ANNOTATION
//						S_PUBLICDATE
//						S_ADDDATE
//						S_SOURCE
//						S_SPHERE
//						S_FIELD
//						S_FILENAME
//						S_FILEPATH
	//	заполняем запрос
	qstrRequest.sprintf("insert into %s values(%d, \'%s\', \'%s\',"
						" \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', \'%s\', "
						"\'%s\', \'%s\')",
						S_TABLENAME, id,
						getSearchParamValue(Title, card).c_str(),
						getSearchParamValue(Author, card).c_str(),
						getSearchParamValue(Word, card).c_str(),
						getSearchParamValue(PublicDate, card).c_str(),
						getSearchParamValue(AddDate, card).c_str(),
						getSearchParamValue(Source, card).c_str(),
						getSearchParamValue(Sphere, card).c_str(),
						getSearchParamValue(Field, card).c_str(),
						getSearchParamValue(FileName, card).c_str(),
						getSearchParamValue(FilePath, card).c_str()
						);

	//	проводим запрос
	if(!queryAdd.exec(qstrRequest))
		throw CANT_WRITE_DTBASE;

	bResult = true;

	//	закрываем соединение
	closeDataBase();

	return	bResult;
}

/******************************************************************************/

// поиск информации в базе данных по определенному параметру
std::list<LiteratureCard> DatabaseMonitor::search(SearchParameter sp,
												  LiteratureCard card)
{
	//	список карточек
	std::list<LiteratureCard>	lstOut;
	
	//	формируем список id карточек
	std::list<long>	lstId;

	//	Формируем строку запроса
	QString	qstrRequest;
	qstrRequest.sprintf("select %s from %s where %s like '%s'",
						S_ID, S_TABLENAME,
						getSearchParamName(sp).c_str(),
						getSearchParamValue(sp, card).c_str());
	
	//	запрос к базе данных

	//	открываем базу данных
	if(!openDataBase())
		throw	CANT_OPEN_DATABASE;
	
	//	получение карточки
	QSqlQuery	findQuery;
	if(!findQuery.exec(qstrRequest))
		throw	CANT_READ_DATABASE;

	QSqlRecord	rec = findQuery.record();

	while(findQuery.next())
	{
		//	заполняем номера id карточек
		long	nId = findQuery.value(rec.indexOf(S_ID)).toInt();
		lstId.push_back(nId);
	}

	closeDataBase();

	//	для каждого id получаем карточку
	std::list<long>::iterator	it;
	for(it = lstId.begin(); it != lstId.end(); ++it)
	{
		try
		{
			long	lId = *it;
			LiteratureCard	tmpCard = getCard(lId);
			lstOut.push_back(tmpCard);
		}
		catch(...)
		{
			throw;
		}
	}

	lstId.clear();

	return	lstOut;
}

/******************************************************************************/

bool DatabaseMonitor::deleteTable()
{
	//	удаление таблицы
	if(QMessageBox::question(NULL, trUtf8("Внимание"), 
						 trUtf8("Вы действительно хотите удалить таблицу?"), 
						 QMessageBox::Yes | QMessageBox::No) ==
	 QMessageBox::No)
	{
		return	false;
	}

	//	формируем запрос
	QString	qstrQuery;
	qstrQuery.sprintf("drop table %s", S_TABLENAME);

	//	открываем базу данных
	if(!openDataBase())
		throw CANT_OPEN_DATABASE;

	//	удаляем таблицу
	QSqlQuery	queryDrop;
	if(!queryDrop.exec(qstrQuery))
		throw	QUERY_ERROR_EX;
	
	closeDataBase();

	return	true;
}

/******************************************************************************/

/**
 * обновить данные по литературной карточке
 * @param card литературная карточка источника
 * @return true в случае успеха
 * @exception CANT_OPEN_DATABASE	- невозможно открыть базу данных
 * @exception CANT_WRITE_DTBASE		- невозможно записать в базу данных
 */
bool DatabaseMonitor::updateCard(LiteratureCard card)
{
	bool	bResult = true;

	if(QMessageBox::question(NULL, trUtf8("Внимание"), 
						 trUtf8("Вы действительно хотите обновить данные?"), 
						 QMessageBox::Yes | QMessageBox::No) ==
	 QMessageBox::No)
	{
		return	false;
	}
	
	//	открываем базу данных
	if(!openDataBase())
		throw	CANT_OPEN_DATABASE;
	
	for(unsigned int i = Title; i <= FilePath; i++)
	{
		//	формируем строку запроса
		QString	strQuery;
		strQuery.sprintf("update %s set %s='%s' where %s=%d",
						 S_TABLENAME,
						 getSearchParamName(i).c_str(),
						 getSearchParamValue(i, card).c_str(),
						 S_ID, card.getID());

		//	производим запрос
		QSqlQuery	query;
		if(!query.exec(strQuery))
			throw	CANT_WRITE_DTBASE;
	}
	
	//	закрываем соединение
	closeDataBase();
	
	return	bResult;
}

/******************************************************************************/

/**
 * открыть соединение с базой данных
 * @return true если соединение открыто
 */
bool DatabaseMonitor::openDataBase()
{
	if(LiteratureBase->isOpen())
		return	true;

	if(!LiteratureBase->open())
		return	false;
	
	return	true;
}

/******************************************************************************/

/**
 * закрыть соединение с базой данных
 * @return true если соединение закрыто
 */
bool DatabaseMonitor::closeDataBase()
{
	if(LiteratureBase->isOpen())
	{
		LiteratureBase->close();
	}
	
	return	true;
}

/******************************************************************************/
